#ifndef __SUBSTANCE_H__
#define __SUBSTANCE_H__

#include <iostream>
#include <math.h>
#include "SubList.h"
#include "common/quaternion.h"
#include "common/common_defs.h"
#include "myIO.h"

static const int Num_of_Comp = 5;
static const float k_Boltz = 1.3804e-14;
static const int MAX_COMP = 128;
static const int ts_n = 5;

static const int MAX_PACK_SIZE = 10;

struct mpi_toSend {
    int sysID;
    int compId;

    Vector3 Pos;
    Vector3 Vel;
    Vector3 Accel;

    float M;
};

///////////////////////////////////////////////
class Particle {
public:
    const Vector3& getPos() const {
        return Pos;
    }
    const Vector3& getVel() const {
        return Vel;
    }
    const Vector3& getAccel() const {
        return Accel;
    }

    float getM() const {
        return M;
    }

    static float t;
    float Ek;
    static IForce** ForceArr;

protected:
    float M;
    float avForce;
    Vector3 Pos;
    Vector3 Vel;
    Vector3 Accel;
    Vector3 extForce;

    // TODO: remove friends
    friend class Molecula;
    friend class TMolecula;
};

class Component: public Particle {
public:

    Component(int molID, const MolInfo& mInfo, float Mass, int cID, float Temp);

    static void InitComponent(const SubListItem& sub);

    float getAvForce();

    int getMolID() const {
        return _molID;
    }
    int getCompID() const {
        return CompID;
    }
    void addExtForce(const Vector3& addForce);

    void addAccel(const Vector3& accel) {
        Accel += accel;
    }

    void newPos();

    void normVel(float Temp, float TempF, int tau);
    void setPos(const Vector3& p1, const Quaternion& twist);
    void getPack(mpi_toSend& pack);

private:
    static int Num_of_comp;
    int CompID;
    int avPeriod;
    int _molID;

};

class Molecula: public Particle {
public:
    Molecula(int ID, const MolDscr& mDscr, const SubListItem& sub, float Temp);
    Molecula() {
    }

    int getID() const {
        return ID;
    }
    int getRank() const {
        return rank;
    }
    Component* getComponent(int index) {
        assert(index < rank);
        return compos[index];
    }
    float getAvForce();

    void newPos();
    void setPos(const Vector3& p, const Quaternion& twist);
    void normVel(float Temp, float TempF, int tau);
    void addExtForce(const Vector3& addForce);

protected:
    Component** compos;
    int rank;
    int ID;
};

class TMolecula: public Molecula {
public:
    TMolecula(int ID, const TubeDscr * mDscr, const SubListItem& sub);

    void newPos();

    void setPos(const Vector3& p, const Quaternion& twist);

protected:
    IForce * distForce;
    float Temp;
    int rTau;
private:
    int NofP;
    int Cicl;
    int OddEven;
};

class Bound {
public:
    virtual ~Bound() {}
    virtual void bCtrl(Component* p)=0;
    virtual bool belon(const Vector3& lV, const Vector3& rV)=0;
    virtual float getPrs() {
        return 0;
    }

    virtual void CalcPrs() {
    }

    static float t;

protected:
    Vector3 Axis, Pos;
    float param1, param2;
    float Sqr, prs;
    float * prsBuf;
    int AvPrsCh, bufP;

    friend class Flow1;
};

typedef Bound* pBound;

class RBound: public Bound {
public:
    RBound(const Vector3& Pos, const Vector3& Axis, float Rad, float H);
    void bCtrl(Component* p);
    bool belon(const Vector3& lV, const Vector3& rV);

};

class PlBound: public Bound {
public:
    PlBound(const Vector3& Pos, const Vector3& Norm, float RadInt, float RadExt, int apc);
    void bCtrl(Component* p);
    bool belon(const Vector3& lV, const Vector3& rV);
    float getPrs();
    void CalcPrs();

};
class Flow1 {
public:
    Flow1(const SubListItem& sub, MolDscr * mDscr, float Prs, float Temp, float Ro, float Rad);
    int addPool(int idCh);
    void prsControl(float g);
    float getPrs();
    void normVel(int tau);
    float getTemp();
    float L_TS;
    float getAvMass() {
        return AvMass;
    }
    Molecula* getAddedMolecula(int index) {
        assert(index < Diver);
        return pBuf[index];
    }
    float getStream();
    ~Flow1();

    static float t_step;

private:
    MolDscr * mDscr;
    SubListItem sub;

    Molecula**   Pool;
    Molecula**   pBuf;
    int Diver;
    int SP, MAX_LEN, CUR, NUM_P;

    float Temp, TempF, Ro, Prs, dRo;
    float AvMass, MaxL;
    float L, Rad;

    float streamMA[5];
    int maTop;
};
//////////////////////////////////

#endif // __SUBSTANCE_H__
